package airAgent.group2.util;

import java.util.ArrayList;
import java.util.Random;
import java.util.ResourceBundle;

import airAgent.group2.Timer;

import ontology.ontology.AirAgentOntology;
import ontology.ontology.CreateAccount;
import ontology.ontology.Direction;
import ontology.ontology.EndOfSimulation;
import ontology.ontology.Money;
import ontology.ontology.ReturnTicketPrice;
import ontology.ontology.StartSimulation;
import ontology.ontology.Ticket;
import ontology.ontology.CancelTicket;
import ontology.ontology.Withdrawn;
import ontology.ontology.CheckTicketPrice;
import ontology.ontology.BuyTicket;
import ontology.ontology.SubscribeTimer;
import ontology.ontology.InformDate;

import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.content.lang.Codec;
import jade.content.lang.Codec.CodecException;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.Ontology;
import jade.content.onto.OntologyException;
import jade.content.onto.basic.Action;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.SimpleBehaviour;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;

import ontology.ontology.Deposit;

public class Util {
	public static final Double BUDGET;
	public static final Double BUDGET_VARIANCE;
	public static final Double CLIENTS_PER_DAY;
	public static final Double CLIENTS_VARIANCE;
	public static final Double AIRPORT_TAXES;
	public static final Double SUBSIDY;
	public static final Double FUEL_COST;
	public static final Double PILOT_SALARY;
	public static final Double HELICOPTER_MAINTENANCE;
	public static final String BANK;
	public static final String AIRLINE;
	public static final String CLIENT;
	public static final String TIMER;
	public static final int SIMULATION_DAYS;
	public static final long TIME_SPEED_RATE; 
	public static final Double CANCELLATION_RATE;

	static {
		ResourceBundle rb = ResourceBundle
				.getBundle("airAgent.group2.configuration");
		BUDGET = Double.parseDouble(rb.getString("CLIENTS_BUDGET"));
		BUDGET_VARIANCE = Double.parseDouble(rb
				.getString("CLIENTS_BUDGET_VARIANCE"));
		CLIENTS_PER_DAY = Double.parseDouble(rb
				.getString("CLIENTS_PER_DAY_RATE"));
		CLIENTS_VARIANCE = Double.parseDouble(rb
				.getString("DAILY_PASSENGERS_VARIANCE"));
		AIRPORT_TAXES = Double.parseDouble(rb.getString("AIRPORT_TAXES"));
		SUBSIDY = Double.parseDouble(rb.getString("SUBSIDY"));
		FUEL_COST = Double.parseDouble(rb.getString("FUEL_COST_OF_A_SINGLE_FLIGHT"));
		PILOT_SALARY = Double.parseDouble(rb.getString("PILOT_SALARY"));
		HELICOPTER_MAINTENANCE = Double.parseDouble(rb.getString("HELICOPTER_MAINTENANCE"));
		SIMULATION_DAYS = Integer.parseInt(rb.getString("SIMULATION_DAYS"));
		TIME_SPEED_RATE = Long.parseLong(rb.getString("TIME_SPEED_RATE"));
		CANCELLATION_RATE = Double.parseDouble(rb.getString("CANCELLATION_RATE"));
		BANK = AirAgentOntology.BANK;
		AIRLINE = AirAgentOntology.AIRLINE;
		CLIENT = AirAgentOntology.CLIENT;
		TIMER = AirAgentOntology.TIMER;
	}

	public static int NumAirlines = 1;
	private static Codec codec = new SLCodec();
	static Ontology ontology = AirAgentOntology.getInstance();
	public static float[] ticketPricesToMontegancedo = new float[SIMULATION_DAYS+5];
	public static float[] ticketPricesToBarajas = new float[SIMULATION_DAYS+5];

	private static ArrayList<Ticket> ReturntTicketList = new ArrayList<Ticket>();

	public static void register(String classname, String name, Agent a) {
		DFAgentDescription dfd = new DFAgentDescription();
		ServiceDescription sd = new ServiceDescription();
		sd.setName(classname);
		sd.setType(name);
		dfd.addServices(sd);
		try {
			DFService.register(a, dfd);
		} catch (FIPAException e) {
			e.printStackTrace();
			System.out.println("Could not register in DF, check Util class");
		}
	}

	public static void CreateAccount(Agent ag, AID bank, Codec codec,
			Ontology ontology) {
		ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
		msg.addReceiver(bank);
		msg.setLanguage(codec.getName());
		msg.setOntology(ontology.getName());
		msg.setProtocol(AirAgentOntology.CREATEACCOUNT);
		CreateAccount ca = new CreateAccount();
		Action agAction = new Action(bank, ca);
		try {
			ag.getContentManager().fillContent(msg, agAction);
			ag.send(msg);
			System.out.println(ag.getLocalName() + " asks for an account");
		} catch (CodecException e) {
			e.printStackTrace();
		} catch (OntologyException e) {
			e.printStackTrace();
		}
	}

	public static void Deposit(Agent ag, AID bank, Codec codec,
			Ontology ontology, float amount) {
		ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
		msg.addReceiver(bank);
		msg.setLanguage(codec.getName());
		msg.setOntology(ontology.getName());
		msg.setProtocol(AirAgentOntology.DEPOSIT);
		Money m = new Money();
		m.setValue(amount);
		Deposit d = new Deposit();
		d.setAmount(m);
		Action agAction = new Action(bank, d);
		try {
			ag.getContentManager().fillContent(msg, agAction);
			ag.send(msg);
			System.out.println(ag.getLocalName() + " Deposits " + amount);
		} catch (CodecException e) {
			e.printStackTrace();
		} catch (OntologyException e) {
			e.printStackTrace();
		}
	}

	public static void Withdraw(Agent ag, AID bank, Codec codec,
			Ontology ontology, float amount) {
		ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
		msg.addReceiver(bank);
		msg.setLanguage(codec.getName());
		msg.setOntology(ontology.getName());
		msg.setProtocol(AirAgentOntology.WITHDRAWN);
		Money m = new Money();
		m.setValue(amount);
		Withdrawn w = new Withdrawn();
		w.setAmount(m);
		Action agAction = new Action(bank, w);
		try {
			ag.getContentManager().fillContent(msg, agAction);
			ag.send(msg);
			System.out.println(ag.getLocalName() + " Withdraws " + amount);
		} catch (CodecException e) {
			e.printStackTrace();
		} catch (OntologyException e) {
			e.printStackTrace();
		}
	}

	public static void CheckTicketPrice(Agent ag, AID airline, Codec codec,
			Ontology ontology, Ticket ticket) {
		ACLMessage msg = new ACLMessage(ACLMessage.QUERY_REF);
		msg.addReceiver(airline);
		msg.setLanguage(codec.getName());
		msg.setOntology(ontology.getName());
		msg.setProtocol(AirAgentOntology.CHECKTICKETPRICE);
		CheckTicketPrice check = new CheckTicketPrice();
		check.setTicketDate(ticket.getTicketDate());
		check.setTicketDirection(ticket.getTicketDirection());
		check.setTicketSeats(ticket.getTicketSeats());
		Action agAction = new Action(airline, check);
		try {
			ag.getContentManager().fillContent(msg, agAction);
			ag.send(msg);
		} catch (CodecException e) {
			e.printStackTrace();
		} catch (OntologyException e) {
			e.printStackTrace();
		}

	}
	
	public static float calculateTicketPrice(Ticket ticket, TicketManagement monthlyTickets, int today){
		
		float price, basePrice;
		if(ticket.getTicketDirection().equals(true)){ //to montegancedo
			if(ticketPricesToMontegancedo[ticket.getTicketDate()] != 0){
				price =ticketPricesToMontegancedo[ticket.getTicketDate()]; 
			}
			else{
				basePrice=(float)( Util.AIRPORT_TAXES +Util.FUEL_COST +((Util.PILOT_SALARY + Util.HELICOPTER_MAINTENANCE) / Util.SIMULATION_DAYS));
				price =  basePrice * (float)1.5;
				if(today > 3){
					if(getAverageNumOfSoldTickets(monthlyTickets, today, ticket.getTicketDirection().getValue()) < 1){
						price =  basePrice * (float)1.1;
					}
					else if(getAverageNumOfSoldTickets(monthlyTickets, today, ticket.getTicketDirection().getValue()) < 2){
						price =  basePrice * (float)1.3;
					}
					else if(getAverageNumOfSoldTickets(monthlyTickets, today, ticket.getTicketDirection().getValue()) < 3){
						price =  basePrice * (float)1.5;
					}
					else if(getAverageNumOfSoldTickets(monthlyTickets, today, ticket.getTicketDirection().getValue()) < 4){
						price =  basePrice * (float)1.7;
					}
					else if(getAverageNumOfSoldTickets(monthlyTickets, today, ticket.getTicketDirection().getValue()) < 5){
						price =  basePrice * (float)1.9;
					}
				}
				ticketPricesToMontegancedo[ticket.getTicketDate()]= price;
			}
		}
		else{			//to Barajas
			if(ticketPricesToBarajas[ticket.getTicketDate()] != 0){
				price =ticketPricesToBarajas[ticket.getTicketDate()]; 
			}
			else{
				basePrice=(float)( Util.AIRPORT_TAXES +Util.FUEL_COST +((Util.PILOT_SALARY + Util.HELICOPTER_MAINTENANCE) / Util.SIMULATION_DAYS));
				price =  basePrice * (float)1.5;
				if(today > 3){
					if(getAverageNumOfSoldTickets(monthlyTickets, today, ticket.getTicketDirection().getValue()) < 1){
						price =  basePrice * (float)1.1;
					}
					else if(getAverageNumOfSoldTickets(monthlyTickets, today, ticket.getTicketDirection().getValue()) < 2){
						price =  basePrice * (float)1.3;
					}
					else if(getAverageNumOfSoldTickets(monthlyTickets, today, ticket.getTicketDirection().getValue()) < 3){
						price =  basePrice * (float)1.5;
					}
					else if(getAverageNumOfSoldTickets(monthlyTickets, today, ticket.getTicketDirection().getValue()) < 4){
						price =  basePrice * (float)1.7;
					}
					else if(getAverageNumOfSoldTickets(monthlyTickets, today, ticket.getTicketDirection().getValue()) < 5){
						price =  basePrice * (float)1.9;
					}
				}
				ticketPricesToBarajas[ticket.getTicketDate()]= price;
			}
		}
		return price;
	}
	
	public static float retrieveTicketPrice(Ticket ticket, TicketManagement monthlyTickets){
		
		float price=0;
		if(ticket.getTicketDirection().equals(true)){ //to montegancedo
			if(ticketPricesToMontegancedo[ticket.getTicketDate()] != 0){
				price =ticketPricesToMontegancedo[ticket.getTicketDate()]; 
			}
		}else {
			if(ticketPricesToBarajas[ticket.getTicketDate()] != 0)
				price =ticketPricesToBarajas[ticket.getTicketDate()];
		}
			return price;
		
	}
	
	public static void ReturnTicketPrice(Agent ag, AID airline, Codec codec,
			Ontology ontology, Ticket ticket, TicketManagement monthlyTickets, int today) {
		Money price = new Money();
		//price.setValue((float) Math.random() * 10);
		price.setValue(calculateTicketPrice(ticket, monthlyTickets, today));
		ACLMessage msg = new ACLMessage(ACLMessage.INFORM);
		msg.addReceiver(airline);
		msg.setLanguage(codec.getName());
		msg.setOntology(ontology.getName());
		msg.setProtocol(AirAgentOntology.RETURNTICKETPRICE);
		ReturnTicketPrice ret = new ReturnTicketPrice();
		ticket.setTicketPrice(price);
		ReturntTicketList.add(ticket);
		ret.setPrice(ticket);
		Action agAction = new Action(airline, ret);
		try {
			ag.getContentManager().fillContent(msg, agAction);
			ag.send(msg);
		} catch (CodecException e) {
			e.printStackTrace();
		} catch (OntologyException e) {
			e.printStackTrace();
		}

	}

	public static void SubscribeTimer(Agent ag, AID timer, Codec codec,
			Ontology ontology) {
		ACLMessage msg = new ACLMessage(ACLMessage.SUBSCRIBE);
		msg.addReceiver(timer);
		msg.setLanguage(codec.getName());
		msg.setOntology(ontology.getName());
		msg.setProtocol(AirAgentOntology.SUBSCRIBETIMER);
		SubscribeTimer sub = new SubscribeTimer();
		Action agAction = new Action(timer, sub);
		try {
			ag.getContentManager().fillContent(msg, agAction);
			ag.send(msg);
			// System.out.println(ag.getLocalName() +
			// " requested to subscribe");
		} catch (CodecException e) {
			e.printStackTrace();
		} catch (OntologyException e) {
			e.printStackTrace();
		}
	}

	public static void addTimerBehavior(Agent ag) {
		ag.addBehaviour(new SimpleBehaviour(ag) {
			private static final long serialVersionUID = 1L;
			private boolean done;

			public void action() {
				DFAgentDescription dfd = new DFAgentDescription();
				ServiceDescription sd = new ServiceDescription();

				sd.setType(Util.TIMER);
				dfd.addServices(sd);

				DFAgentDescription[] res = new DFAgentDescription[1];
				try {
					res = DFService.search(myAgent, dfd);
				} catch (FIPAException e) {
					e.printStackTrace();
				}
				if (res.length > 0) {
					AID timer = (AID) res[0].getName();
					Util.SubscribeTimer(myAgent, timer, codec, ontology);
					done = true;
				} else {
					System.out.println("Failed to find the Timer, retrying...");
					// doWait(5000);
				}
			}

			@Override
			public boolean done() {
				return done;
			}
		});

	}

	public static void InformDate(Agent ag, AID airline, Codec codec,
			Ontology ontology) {

		ACLMessage msg = new ACLMessage(ACLMessage.INFORM);
		msg.addReceiver(airline);
		msg.setLanguage(codec.getName());
		msg.setOntology(ontology.getName());
		msg.setProtocol(AirAgentOntology.INFORMDATE);
		InformDate inf = new InformDate();
		inf.setDate(Timer.day);
		Action agAction = new Action(airline, inf);
		try {
			ag.getContentManager().fillContent(msg, agAction);
			ag.send(msg);
		} catch (CodecException e) {
			e.printStackTrace();
		} catch (OntologyException e) {
			e.printStackTrace();
		}

	}

	public static void CancelTicket(Agent ag, AID airline, Codec codec,
			Ontology ontology, Ticket ticket) {
		ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
		msg.addReceiver(airline);
		msg.setLanguage(codec.getName());
		msg.setOntology(ontology.getName());
		msg.setProtocol(AirAgentOntology.CANCELTICKET);
		CancelTicket buy = new CancelTicket();
		buy.setTicketDate(ticket.getTicketDate());
		buy.setTicketDirection(ticket.getTicketDirection());
		buy.setTicketSeats(ticket.getTicketSeats());
		Action agAction = new Action(airline, buy);
		try {
			ag.getContentManager().fillContent(msg, agAction);
			ag.send(msg);
		} catch (CodecException e) {
			e.printStackTrace();
		} catch (OntologyException e) {
			e.printStackTrace();
		}

	}

	public static void BuyTicket(Agent ag, AID airline, Codec codec,
			Ontology ontology, Ticket ticket) {
		ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
		msg.setLanguage(codec.getName());
		msg.setOntology(ontology.getName());
		msg.setProtocol(AirAgentOntology.BUYTICKET);
		
		airline = ticket.getTicketAirlineID();
		msg.addReceiver(airline);

		BuyTicket buy = new BuyTicket();
		buy.setTicketDate(ticket.getTicketDate());
		buy.setTicketDirection(ticket.getTicketDirection());
		buy.setTicketSeats(ticket.getTicketSeats());
		Action agAction = new Action(airline, buy);
		try {
			ag.getContentManager().fillContent(msg, agAction);
			ag.send(msg);
		} catch (CodecException e) {
			e.printStackTrace();
		} catch (OntologyException e) {
			e.printStackTrace();
		}
	}

	public static void StartSimulation(Agent ag, AID airline, Codec codec,
			Ontology ontology) {
		ACLMessage msg = new ACLMessage(ACLMessage.INFORM);
		msg.addReceiver(airline);
		msg.setLanguage(codec.getName());
		msg.setOntology(ontology.getName());
		msg.setProtocol(AirAgentOntology.STARTSIMULATION);
		StartSimulation start = new StartSimulation();
		Action agAction = new Action(airline, start);
		try {
			ag.getContentManager().fillContent(msg, agAction);
			ag.send(msg);
		} catch (CodecException e) {
			e.printStackTrace();
		} catch (OntologyException e) {
			e.printStackTrace();
		}
	}

	public static void EndSimulation(Agent ag, AID airline, Codec codec,
			Ontology ontology) {
		ACLMessage msg = new ACLMessage(ACLMessage.INFORM);
		msg.addReceiver(airline);
		msg.setLanguage(codec.getName());
		msg.setOntology(ontology.getName());
		msg.setProtocol(AirAgentOntology.ENDOFSIMULATION);
		EndOfSimulation end = new EndOfSimulation();
		Action agAction = new Action(airline, end);
		try {
			ag.getContentManager().fillContent(msg, agAction);
			ag.send(msg);
		} catch (CodecException e) {
			e.printStackTrace();
		} catch (OntologyException e) {
			e.printStackTrace();
		}
	}

	public static Ticket initializeTicket(AID airlineID, AID clientID,
			int date, Direction dir, Money price, int numSeats, boolean search) {
		Ticket ticket = new Ticket();
		ticket.setTicketAirlineID(airlineID);
		ticket.setTicketDate(date);
		ticket.setTicketDirection(dir);
		ticket.setTicketSeats(numSeats);
		if (price != null)
			ticket.setTicketPrice(price);
		ticket.setTicketClientID(clientID);

		if (search) {
			for (int i = 0; i < ReturntTicketList.size(); i++) {
				if (ReturntTicketList.get(i).getTicketAirlineID().getName() == airlineID
						.getName()
						&& ReturntTicketList.get(i).getTicketClientID().equals(clientID)
						&& ReturntTicketList.get(i).getTicketDate() == date
						&& ReturntTicketList.get(i).getTicketDirection()
								.getValue() == dir.getValue()
						&& ReturntTicketList.get(i).getTicketSeats() == numSeats)
					ticket.setTicketPrice(ReturntTicketList.get(i)
							.getTicketPrice());
			}
		}

		return ticket;
	}

	public static DFAgentDescription[] search(Agent myAgent, String type) {
		DFAgentDescription dfd = new DFAgentDescription();
		ServiceDescription sd = new ServiceDescription();
		sd.setType(type);
		dfd.addServices(sd);
		try {
			return DFService.search(myAgent, dfd);
		} catch (FIPAException e) {
			e.printStackTrace();
		}
		return null;
	}

	public static double getRandom(double rate, double variance) {
		double min = rate - variance;
		double max = rate + variance;
		double range =  max - min + 1;
		double fraction = (range * new Random().nextDouble());
		return fraction + min;
	}
	
	public static double calculateTotalFuelCost(TicketManagement monthlyTickets){
		
		double cost=0;
		for (int i=1; i< Util.SIMULATION_DAYS; i++){
			cost += (float)(Util.FUEL_COST * monthlyTickets.getTotalNumberOfSoldTicketsForDay(i));
		}
		
		return cost;
	}
	
	public static int calculateTotalFlightsPerformed(TicketManagement monthlyTickets){
		
		int count=0;
		for (int i=1; i< Util.SIMULATION_DAYS; i++){
			if(monthlyTickets.getTotalNumberOfSoldTicketsForDayForDirection(i, true) > 0);
				count++;
			if(monthlyTickets.getTotalNumberOfSoldTicketsForDayForDirection(i, false) > 0);
				count++;
		}
		
		return count;
	}
	
	public static float getAverageNumOfSoldTickets(TicketManagement monthlyTickets, int day, boolean dir){
		float avg=0;
		for(int i=0;i<day;i++){
			avg += monthlyTickets.getTotalNumberOfSoldTicketsForDayForDirection(day, dir);
		}
		return (avg/day);
	}
	
	public static float calculateOtherCosts(TicketManagement monthlyTickets){
		
		float cost=0;
		for (int i=1; i< Util.SIMULATION_DAYS; i++){
			cost += (float)(Util.AIRPORT_TAXES * monthlyTickets.getTotalNumberOfSoldTicketsForDay(i));
		}
		cost +=  Util.HELICOPTER_MAINTENANCE + Util.PILOT_SALARY;
		
		return cost;
	}
	
	public static float calculateSubsidy(TicketManagement monthlyTickets){
		
		float cost=0;
		for (int i=1; i< Util.SIMULATION_DAYS; i++){
			if(5 == monthlyTickets.getTotalNumberOfSoldTicketsForDayForDirection(i,true))
				cost += Util.SUBSIDY;
			if(5 == monthlyTickets.getTotalNumberOfSoldTicketsForDayForDirection(i,false))
				cost += Util.SUBSIDY;
		}
		
		return cost;
	}
}
